#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdlib>
#include <pthread.h>
using namespace std;

const int MAX_EVENT_NUMBER = 1024;
const int BUFFER_SIZE = 10;

int setNonBlocking(int fd) {
    int oldOption = fcntl(fd, F_GETFL);
    int newOption = oldOption | O_NONBLOCK;
    fcntl(fd, F_SETFL, newOption);
    return oldOption;
}

void addFd(int epollFd, int fd, bool enableET) {
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    if (enableET) {
        event.events |= EPOLLET; // Enable Edge Triggered mode
    }
    epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event);
    setNonBlocking(fd);
}

void LT(epoll_event* events, int number, int epollFd, int listenFd) {
    char buf[BUFFER_SIZE];
    for(int i = 0; i < number; ++i) {
        int sockFd = events[i].data.fd;
        if (sockFd == listenFd) {
            struct sockaddr_in client;
            socklen_t clientLen = sizeof(client);
            int connFd = accept(listenFd, (struct sockaddr*)&client, &clientLen);
            addFd(epollFd, connFd, false);  // 使用LT模式
        } 
        else if (events[i].events & EPOLLIN) {
            cout << "event triggered on fd: " << sockFd << endl;
            memset(buf, 0, BUFFER_SIZE);
            int ret = recv(sockFd, buf, BUFFER_SIZE - 1, 0);
            if (ret <= 0) {
                close(sockFd);
                continue;
            }
            cout << "get " << ret << " bytes of content: " << buf << endl;
        }
        else {
            cout << "some other event occurred on fd: " << sockFd << endl;
        }
    }
}

void ET (epoll_event* events, int number, int epollFd, int listenFd) {
    char buf[BUFFER_SIZE];
    for(int i = 0; i < number; ++i) {
        int sockFd = events[i].data.fd;
        if (sockFd == listenFd) {
            struct sockaddr_in client;
            socklen_t clientLen = sizeof(client);
            int connFd = accept(listenFd, (struct sockaddr*)&client, &clientLen);
            addFd(epollFd, connFd, true);   // 使用ET模式
        } 
        else if (events[i].events & EPOLLIN) {
            cout << "event triggered once on fd: " << sockFd << endl;
            // 不会重复触发，需要循环读取数据，直到数据被完全读取
            while (true) {
                memset(buf, 0, BUFFER_SIZE);
                int ret = recv(sockFd, buf, BUFFER_SIZE - 1, 0);
                if (ret < 0) {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) {
                        cout << "read later" << endl;
                        break;
                    }
                    close(sockFd);
                    break;
                } 
                else if (ret == 0) {
                    close(sockFd);
                }
                else {
                    cout << "get " << ret << " bytes of content: " << buf << endl;
                }
            }
        }
        else {
            cout << "some other event occurred on fd: " << sockFd << endl;
        }
    }
}

int main(int argc, char* argv[])
{
    if(argv <= 2) {
        cerr << "Usage: " << argv[0] << " <IP> <Port>" << endl;
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenFd = socket(AF_INET, SOCK_STREAM, 0);
    assert(listenFd >= 0);

    int ret = bind(listenFd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(listenFd, 5);
    assert(ret != -1);

    int epollFd = epoll_create(5);
    assert(epollFd != -1);
    epoll_event events[MAX_EVENT_NUMBER];
    addFd(epollFd, listenFd, true);  // 使用ET模式

    while (true) {
        int ret = epoll_wait(epollFd, events, MAX_EVENT_NUMBER, -1);
        if (ret < 0 && errno != EINTR) {
            cerr << "epoll failure: " << strerror(errno) << endl;
            break;
        }
        
        LT(events, ret, epollFd, listenFd);         // 使用LT模式
        // ET(events, number, epollFd, listenFd);   // 使用ET模式
    }

    close(listenFd);
    close(epollFd);
    return 0;
}